from robot.parsing.model import Step, ForLoop, UserKeyword
from rsa.util.robot_utilities import get_full_name
from rsa.util.common_utilities import format_severity

class Rule(object):
    """
    This is the basic Rule interface for validation rules.
    """
                        
    def apply(self, node, symbols):
        """
        Apply this rule to the given test
        """
        raise NotImplementedError("Should have implemented this")
    
class TemplateRule(Rule):
    """
    Abstract representation of a rule, it implements the method apply and visit, leaving for 
    the child's classes the implementation of the methods visit_step, visit_loop, start and end. 
    """    
       
    def apply(self, node, symbols):
        """
        Apply the rule for each step of the test
        """
        self.line = 0
        self.node = node
        self.symbols = symbols
        self.start()
        for step in node.steps:
            self.line += 1
            self.visit(step)
        self.end()

    def visit(self, node):
        """
        Visit a node and checks if it applies the rule
        """
        if(isinstance(node, Step)):
            if(node.keyword != "Comment"):
                self.visit_step(node)
        elif(isinstance(node, ForLoop)):
            self.visit_loop(node)
        else:
            raise Exception('Invalid node type' + node)
        
    def start(self):
        """
        Start processing. Called once, before apply() is first called. 
        """
        pass

    def end(self):
        """
        End processing. Called once, after apply() is last called. 
        """
        pass
    
    def visit_step(self, step):
        """
        Apply this rule to a given step
        """
        raise NotImplementedError("Should have implemented this")
    
    def visit_loop(self, loop):
        """
        Apply this rule to a given loop
        """
        raise NotImplementedError("Should have implemented this")
    
class RuleViolation(object):
    """
    Represents a rule violation in the suites
    """
        
    def __init__(self, rule, step, element=None, test=None, line=None):
        """
        Initializes the class RuleViolation
        @param rule: the rule violated
        @param step: the step where the violation occurred (None value allowed)
        @param element: the element that triggers the violation
        @param test: the test case where the violation was found
        @param line: the line when the violation occurred
        @param solution: a suggested solution to the violation
        """
        self.rule = rule
        self.step = step
        self.test = test
        self.line = self.parse_line(line)
        self.element = element
        
    def __str__(self):
        """
        Print the rule violation in the format TestCase[line]: violation message - cause 
        """
        return self.print_violation(["-solution"])
    
    def format_element(self, element, property):
        if(not property): return ""
        if(isinstance(element, list)):
            elements = dict()
            for i in range(len(element)):
                elements["item" + str(i + 1)] = element[i]
            return property.safe_substitute(elements)
        return property.safe_substitute(item=element)
    
    def print_violation(self, mode=[]):
        """
        Print the rule violation in the format TestCase[line]: violation message - cause 
        """
        rule = self.rule.name
        name = get_full_name(self.test)
        message = self.format_element(self.element, self.rule.message)
        solution = self.format_element(self.element, self.rule.solution)
        severity = format_severity(self.rule.severity)
        if(self.line != None):
            line = "[" + str(self.line) + "]"
        else:
            line = ""
        return "[" + severity + "]: "+ "[" + rule + "] " + name + line + ": " + message + " | " + solution
    
    def parse_line(self, line):
        """
        Parse the line to display the proper information, depending the type of element
        where the violation was found, and if headers of the element.
        """
        if line != None and line != 0:
            extra_lines = 0
            if(isinstance(self.test, UserKeyword)):
                if self.test.timeout.value != None:
                    extra_lines = extra_lines + 1
                if len(self.test.args.value) >0:
                    extra_lines = extra_lines + 1
                if len(self.test.return_.value) >0:
                    extra_lines = extra_lines + 1
            elif self.test != None:
                if self.test.tags.value != None:
                    extra_lines = extra_lines + 1
                if self.test.teardown.name != None:
                    extra_lines = extra_lines + 1
                if self.test.template.value != None:
                    extra_lines = extra_lines + 1
                if self.test.timeout.value != None:
                    extra_lines = extra_lines + 1
                if self.test.setup.name != None:
                    extra_lines = extra_lines + 1   
            return line + extra_lines
        else:
            return line
